#pragma once
#include "../Ode.h"

namespace Skill
{
	namespace Ode
	{
		ref class DVector3;
		ref class DVector4;
		ref class DQuaternion;

		public ref class DMatrix3 : DIUnmanagedPointer
		{
			INTERNAL_CLASS_DECLARATION(DMatrix3,dReal);
		public:
			UNMANAGEDPOINTER_DECLARATION();

			DMatrix3()
			{
				_SetPointer(new dMatrix3(),true);				
			}

			property dReal default[int]
			{
				dReal get(int index);
				void set(int index,dReal value);
			}

			property dReal default[int,int]
			{
				dReal get(int i,int j);
				void set(int i,int j,dReal value);
			}


			VALUE_PROP_DECLARATION_GETSET(dReal,M11);
			VALUE_PROP_DECLARATION_GETSET(dReal,M21);
			VALUE_PROP_DECLARATION_GETSET(dReal,M31);
			VALUE_PROP_DECLARATION_GETSET(dReal,M41);

			VALUE_PROP_DECLARATION_GETSET(dReal,M12);
			VALUE_PROP_DECLARATION_GETSET(dReal,M22);
			VALUE_PROP_DECLARATION_GETSET(dReal,M32);
			VALUE_PROP_DECLARATION_GETSET(dReal,M42);

			VALUE_PROP_DECLARATION_GETSET(dReal,M13);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M23);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M33);						
			VALUE_PROP_DECLARATION_GETSET(dReal,M43);


		public:
			static VALUE_PROP_DECLARATION_GET(DMatrix3^,Identity);
			void SetIdentity();

			void SetFromAxisAndAngle(dReal ax, dReal ay, dReal az, dReal angle);
			void SetFromAxisAndAngle(DVector3^ axis, dReal angle);
			void SetFromAxisAndAngle(DVector4^ axis, dReal angle);

			static DMatrix3^ FromAxisAndAngle(dReal ax, dReal ay, dReal az, dReal angle);
			static DMatrix3^ FromAxisAndAngle(DVector3^ axis, dReal angle);
			static DMatrix3^ FromAxisAndAngle(DVector4^ axis, dReal angle);
			
			void SetFromEulerAngles (dReal phi, dReal theta, dReal psi);
			static DMatrix3^ FromEulerAngles(dReal phi, dReal theta, dReal psi);

			void SetFrom2Axes(dReal ax, dReal ay, dReal az, dReal bx, dReal by, dReal bz);
			void SetFrom2Axes(DVector3^ a, DVector3^ b);
			void SetFrom2Axes(DVector4^ a, DVector4^ b);

			static DMatrix3^ From2Axes(dReal ax, dReal ay, dReal az, dReal bx, dReal by, dReal bz);
			static DMatrix3^ From2Axes(DVector3^ a, DVector3^ b);
			static DMatrix3^ From2Axes(DVector4^ a, DVector4^ b);

			void SetFromZAxis(dReal ax, dReal ay, dReal az);
			void SetFromZAxis(DVector3^ a);
			void SetFromZAxis(DVector4^ a);

			static DMatrix3^ FromZAxis(dReal ax, dReal ay, dReal az);
			static DMatrix3^ FromZAxis(DVector3^ a);
			static DMatrix3^ FromZAxis(DVector4^ a);

			void SetFromQuaternion(DQuaternion^ q);
			static DMatrix3^ FromQuaternion(DQuaternion^ q);

			///<summary> set a matrix to all zeros </summary>
			void SetZero();

			///<summary> set a matrix to specific value </summary>
			void SetValue(dReal value);

			///<summary>
			/// matrix multiplication. all matrices are stored in standard row format.
			/// the digit refers to the argument that is transposed:
			///   0:   A = B  * C   (sizes: A:p*r B:p*q C:q*r)
			///   1:   A = B' * C   (sizes: A:p*r B:q*p C:q*r)
			///   2:   A = B  * C'  (sizes: A:p*r B:p*q C:r*q)
			/// case 1,2 are equivalent to saying that the operation is A=B*C but
			/// B or C are stored in standard column format.
			///</summary>
			static void Multiply0(DMatrix3^ A, DMatrix3^ B, DMatrix3^ C)
			{
				dMultiply0(A->_This,B->_This,C->_This,3,3,3);
			}
			static void Multiply1(DMatrix3^ A, DMatrix3^ B, DMatrix3^ C)
			{
				dMultiply1(A->_This,B->_This,C->_This,3,3,3);
			}
			static void Multiply2(DMatrix3^ A, DMatrix3^ B, DMatrix3^ C)
			{
				dMultiply1(A->_This,B->_This,C->_This,3,3,3);
			}

			virtual String^ ToString() override
			{
				return String::Format("{{{{M11:{0} M12:{1} M13:{2}}} {{M21:{3} M22:{4} M23:{5}}} {{M31:{6} M32:{7} M33:{8}}} {{M41:{9} M42:{10} M43:{11}}}}}",M11,M12,M13,M21,M22,M23,M31,M32,M33,M41,M42,M43);
			}
		};
	}
}